local oo = require("SharedLibs.BaseLib")
local co = require("SharedLibs.CoroLib")

Debugger = require("SharedLibs.Debugger");
AsyncOp = require("SharedLibs.AsyncOp");
Seq = require("SharedLibs.FP.Sequence");

CU = require("SharedLibs.CoUtils");
MU = require("SharedLibs.Math.MathUtil");
TM = require("SharedLibs.TimerMgr");

_IsStudio = _IsStudio;
----------
local SelectionRule = {};
co.SelectionRule = SelectionRule;

--[[
	interrupt规则: 
		高优先级可以打断低优先级的
		低优先级仅在高优先级的值为false时才可打断高优先级的
	check规则: 
		高优先级优先Check,
]]  
SelectionRule.Priority = 1; 
--[[
	interrupt规则: 
		只要当前Action对应的值为false, 就可以被打断
	check规则: 
		按照顺序轮流(都有机会被优先Check)
]]
SelectionRule.RoundRobin = 2;
--- 由用户决定打断优先级以及Check优先级.
--- 上层需要指定 打断检查(_I) 和 执行检查(_C) 两个回调
--- 如果不指定, 那默认行为和Priority是一样的(类似行为树的逻辑)
SelectionRule.Custom = 3;

--- Priority rule's interrupt
function _PriorityI(action, curAction)
	if action._priority <= curAction._priority then
		return true;
	else
		return not curAction._property:GetValue();
	end
end

--- Priority rule's check
function _PriorityC(map)
	--- 找到property值为true, 并且priority最高(值最小)的返回
	local p, a = nil, nil;
	local priority = MU.MAX_INT;

	for prop, action in pairs(map) do
		if not prop:GetValue() then goto continue end;

		local pri = action._priority;
		if pri < priority then
			priority = pri;
			p = prop;
			a = action;
		end

		::continue::
	end
	return p, a;
end

--- RoundRobin rule's interrupt
function _RoundRobinI(action, lastAction)
	return true;
end

--- RoundRobin rule's check
function _RoundRobinC(map)

end

--[[
Selector, 即时序树(类似决策树或行为树)
Selector对Property进行分发以执行不同的Action(AsyncOp)
它本身不会停止! 你可以手动Stop它或者通过把它和其它Op组合来使它停止.
例如:
	WaitOneOf(Selector, signal) --- signal发生就立刻停止Selector

注：
	1. Action是可以同步执行的, 但是要注意有可能会导致死循环! 
	2. Selector内部某时刻只会有一个Action被执行(_curAction), 如果要同时执行
	多个Action，应该使用Parallel或其他支持并行的CombinedOp
	3. Selctor和DecisionTree的区别(AI/DecisionTree/Tree)
		a. Selector是由事件驱动的, 而DecisionTree是每帧都要遍历树, 寻找可以执行的Node. Selctor的效率要好的多.
	    b. Selector的action就是普通的AsyncOp, 而DecisionTree里还额外封装了一个AsyncAction. Selector更简洁.
		c. Selector的guardContion就是普通的property, 而DecisionTree里额外封装了IGuard. Selector更简洁.
]]
co.Selector = oo.Class();
local Selector = co.Selector;
Selector.name = "Selector";
oo.ClassExtends(Selector, AsyncOp);

--- 注：Selector无法直接Select()。因为要设置的参数太多，所以必须先NewSelector，然后Wait()
function co.NewSelector(name, rule)
	return oo.New(Selector, name, rule);
end

---
function Selector.prototype:constructor(name, rule)
	Selector._ss.prototype.constructor(self)
	
	--- 外部可以设置的事件回调
	self.onStart = nil;
	self.onStopped = nil;
	--- 当一个Action被执行前会先执行这个回调
	self.onExecute = nil;

	self.name = name or "Selector";
	self._state = co.AsyncOpState.Ready;

	--- 打断和Check优先级规则
	self._rule = rule or SelectionRule.Priority;
	--- 用来做 打断优先级判断 的函数(无状态/有状态均可)
	self._I = nil;
	--- 用来判断 哪个可以执行 的函数(无状态/有状态均可)
	self._C = nil;
	self:_initRuleFuncs();

	--- key: signal, value: action
	self._propMap = {};
	self._count = 0;

	--- 如果一个Action已经在执行, 是否停止它然后重新执行
	--- true表示覆盖(重新执行)
	self.overrideAction = false;

end

function Selector.prototype.OnStart(self, onStart)
	self.onStart = onStart;
	return self;
end

function Selector.prototype.OnStopped(self, onStopped)
	self.onStopped = onStopped;
	return self;
end

function Selector.prototype:_initRuleFuncs()
	if self._rule == SelectionRule.Priority then
		self._I = _PriorityI;
		self._C = _PriorityC;
	elseif self._rule == SelectionRule.RoundRobin then
		self._I = _RoundRobinI;
		self._C = _RoundRobinC;
	elseif self._rule == SelectionRule.Custom then
		--- 默认行为和Priority一样
		self._I = _PriorityI;
		self._C = _PriorityC;
	end
end

function Selector.prototype:SetI(func)
	self._I = func;
	return self;
end

function Selector.prototype:SetC(func)
	self._C = func;
	return self;
end

--[[
给Selector增加一个case
注意:
	1. property的值必须是bool型的
	2. action 必须是 ready 状态的 AsyncOp 或者是一个可以被转换为Op的对象（例如一个function）	
	3. isExclusive 为 true 时, 当该 Action 还在执行时不会被打断(收到信号都忽略掉)
	4. action 的 priorit 就是加入的先后顺序, 先增加的优先级更高!!!
]]
function Selector.prototype:Case(property
	, action, isExclusive)
	assert(self._state == co.AsyncOpState.Ready);
	isExclusive = isExclusive or false;
	
	local asyncOp = CU.ToOp(action);
	assert(not asyncOp._isExclusive and not asyncOp._priority);
	asyncOp._isExclusive = isExclusive;
	--- Priority越小, 优先级越高
	asyncOp._priority = self._count;
	asyncOp._property = property;

	self._propMap[property] = asyncOp;

	self._count = self._count + 1;

	return self;
end

oo.SetDescriptor(
	Selector.prototype,
	"isCombinedOp", { get = function(self) return true; end },
	true
)

function Selector.prototype:_onStart()
	if self.onStart then
		self.onStart(self);
	end

	for property, action in pairs(self._propMap) do
		local handler = self:_getHandler(property, action);		
		--- 注：这里只能保存在action里，不能保存在property里
		--- 因为多个Selector可以引用同一个property，但action是不可复用的。
		action.__sHandler = handler; 
		property:On(handler);
	end
	
	--- 注意: 即使启动时没有任何case分支可以执行,也不是问题. 因为之后某个condition
	--- 还是有可能触发的(即使一直没有触发,也认为这是上层期望的)
	self:_checkNExecute();
end

function Selector.prototype:_getHandler(property, action)
	return function(val)
		--- 只有val为真时, 才有可能触发Action执行 
		if not val then return end;
		if self._state ~= co.AsyncOpState.Executing then return end;

		if self._inExclusive then return end;

		if self._curAction == action 
			and not self.overrideAction then
			return;
		end

		if self._curAction then
			--- 根据规则来检查是否可以打断当前Action
			if self._I(action, self._curAction) then
				self._curAction:Stop();
				self._curAction = nil;

				self:_execute(property, action);					
			end
		else
			--- 当前不在执行其它Action, 那一定可以执行这个Action
			self:_execute(property, action);				
		end
	end;
end

function Selector.prototype:GetCurOps()
	return Seq.fromYield(function(yield)
		if self._curAction == nil then
			return;
		else
			coroutine.yield(self._curAction);
		end
	end);
end

--- 遍历检查(检查规则由_rule决定)所有property, 如果为true, 则立刻执行(退出遍历)
function Selector.prototype:_checkNExecute()
	local property, action = self._C(self._propMap);
	if property == nil then return end;

	assert(action ~= nil);
	self:_execute(property, action);
end

function Selector.prototype:_execute(property, action)
	local iFrame = TM.iFrame;
	
	local onActionEnd = function()
		if _IsStudio then
			if TM.iFrame - iFrame == 0 then
				local actionInfo = "ActionInfo: "..action:GetDesc();
				
				--- Sync-action指一个Action同步执行完毕了, 没有任何异步发生
				--- 这有可能会导致死循环. 但也可能是期望的效果!
				warn("Sync-action detected! SelectorName:"..self.name
					..actionInfo
					..", ConditionIndex: "..action._priority
					.." , ConditionName: "..property.name.."\n");
			end
		end
		
		self._curAction = nil;

		if action._isExclusive then
			self._inExclusive = false;
		end

		--- 检查有没有可以执行的, 有则立刻执行
		self:_checkNExecute();
	end

	self._curAction = action;
	self._inExclusive = action._isExclusive;

	if self.onExecute then
		self.onExecute(property, action);
	end

	action:Reset();
	action:Start(onActionEnd, self, true);
end

function Selector.prototype:_onStop()
	for signal, action in pairs(self._propMap) do
		signal:Off(action.__sHandler);
		action.__sHandler = nil;
	end	

	if self._curAction then	
		self._curAction:Stop();
		self._curAction = nil;
	end
	
	self:_unhookBadSignals();

	if self.onStopped then
		self.onStopped(self);
	end
end

function Selector.prototype:_onReset()
	self:_unhookBadSignals();
end

function Selector.prototype:_onPause()
	if self._curAction then	
		self._curAction:Pause();
	end

	self:_hookBadSignal();	
end

function Selector.prototype:_onResume()
	self:_unhookBadSignals();

	if self._curAction then	
		self._curAction:Resume();
	end
end

function Selector.prototype:GetReturn()
	return nil;
end

----
--- 以后如果支持动态Add/Remove一个Case时, 需要实现该方法
function Selector.prototype:_unhookBadSignal()
	assert(false);
end

function Selector.prototype:_onBadChange()
	warn("Should not change property when Selector paused!");
end

function Selector.prototype:_unhookBadSignals()
	if not _IsStudio then return end;

	if self._badSignalHooked then
		self._badSignalHooked  = false;
		--- 暂停后, 所有的Property都不允许改变
		--- 这里在studio里做一个检测以预防出错
		for property, _ in pairs(self._propMap) do
			property:Off(self._onBadSignalBind);
		end
	end
end

function Selector.prototype:_hookBadSignal()
	if not _IsStudio then return end;

	if _IsStudio and not self._badSignalHooked then
		self._badSignalHooked = true;

		if not self._onBadSignalBind then
			self._onBadSignalBind = oo.bind(self._onBadChange, self)
		end

		--- 暂停后, 所有的Property都不允许改变
		--- 这里在studio里做一个检测以预防出错
		for property, _ in pairs(self._propMap) do
			property:On(self._onBadSignalBind);
		end
	end 
end

return Selector;
